/*
    Copyright (C) 2009 Giacomo Spigler

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>

#include <cv.h>

#include <cutil.h>

#include "standard.h"
#include "gabor.h"



#define writebyte(a__, fp__) fwrite(&a__, sizeof(unsigned char), 1, fp__);
#define writeint(a__, fp__) fwrite(&a__, sizeof(int), 1, fp__);
#define writefloat(a__, fp__) fwrite(&a__, sizeof(float), 1, fp__)

#define readbyte(a__, fp__) fread(&a__, sizeof(unsigned char), 1, fp__);
#define readint(a__, fp__) fread(&a__, sizeof(int), 1, fp__);
#define readfloat(a__, fp__) fread(&a__, sizeof(float), 1, fp__)




extern "C" {


void InitWidthsHeights(int w_host[NUMSCALES], int h_host[NUMSCALES], int **w_dev, int **h_dev) {
  cudaMalloc((void**)w_dev, NUMSCALES*sizeof(int));
  cudaMalloc((void**)h_dev, NUMSCALES*sizeof(int));

  cudaMemcpy(*w_dev, w_host, NUMSCALES*sizeof(int), cudaMemcpyHostToDevice);
  cudaMemcpy(*h_dev, h_host, NUMSCALES*sizeof(int), cudaMemcpyHostToDevice);

}


void SetPyramid_0(IplImage *im, IplImage *pyr[NUMSCALES], unsigned char *pyr_dev, unsigned int pyr_dev_pitch) {
  cvCvtColor(im, pyr[0], CV_BGR2GRAY);

  cudaMemcpy2D(pyr_dev, pyr_dev_pitch, pyr[0]->imageData, pyr[0]->widthStep, pyr[0]->width, pyr[0]->height, cudaMemcpyHostToDevice);


  for(int i=1; i<NUMSCALES; i++) {
    int base=i*pyr_dev_pitch*pyr[0]->height;

    cvResize(pyr[i-1], pyr[i], CV_INTER_CUBIC);

    cudaMemcpy2D(pyr_dev+base, pyr_dev_pitch, pyr[i]->imageData, pyr[i]->widthStep, pyr[i]->width, pyr[i]->height, cudaMemcpyHostToDevice);
  }


//to check
/*
cvZero(pyr[0]);
cudaMemcpy2D(pyr[0]->imageData, pyr[0]->widthStep, pyr_dev+9*pyr_dev_pitch*pyr[0]->height, pyr_dev_pitch, pyr[9]->width, pyr[9]->height, cudaMemcpyDeviceToHost);
*/

}


void InitPyramidBuffer(IplImage *pyr[NUMSCALES], unsigned char **pyr_dev, unsigned char *s1_dev[4], unsigned char *c1_dev[4], unsigned int *pitch, unsigned int *c1_pitch, unsigned char **s2_dev, int nums2, unsigned int *s2_pitch) {
  cudaMallocPitch((void**)pyr_dev, pitch, pyr[0]->width*sizeof(unsigned char), pyr[0]->height*NUMSCALES);


  int c1_w=(pyr[0]->width-2*GABOR_W-C1_SIZE)/C1_STEP+1;
  int c1_h=(pyr[0]->height-2*GABOR_W-C1_SIZE)/C1_STEP+1;
  for(int i=0; i<4; i++) {
    cudaMallocPitch((void**)&(s1_dev[i]), pitch, pyr[0]->width*sizeof(unsigned char), pyr[0]->height*NUMSCALES);

    cudaMallocPitch((void**)&(c1_dev[i]), c1_pitch, c1_w*sizeof(unsigned char), c1_h*(NUMSCALES-1));
  }


  //s2 buffers will have the same size for every possible patch size, but out-of-boundaries for big patches will be zeroed
  // ???????????????
  //TO ACCESS THIS BUFFER:  every scale is a (c1_w-4)*nums2 x (c1_h-4)  block. there are 9 such blocks.  within every block, every single pixel is nums2-dimensions-big
  //PROBLEM : will it bind to a texture?  eventually 1D texture?
  cudaMallocPitch((void**)s2_dev, s2_pitch, (c1_w-4)*nums2, (c1_h-4)*(NUMSCALES-1));


}


void LoadS2Patches(const char *file, int *nums2, int *n4, int *n8, unsigned char **patch4x4, unsigned char **patch8x8) {
  FILE *fp=fopen(file, "r");

  int a, b, c, d;
  readint(a, fp);
  readint(b, fp);
  readint(c, fp);
  readint(d, fp);

  *n4=a;
  *n8=b;
  *nums2=a+b;


  cudaMalloc((void**)patch4x4, a*64*sizeof(unsigned char));
  cudaMalloc((void**)patch8x8, b*256*sizeof(unsigned char));

  unsigned char *p4=(unsigned char *)malloc(a*64*sizeof(unsigned char)); // for x, y, ori
  unsigned char *p8=(unsigned char *)malloc(b*256*sizeof(unsigned char));


  fread(p4, sizeof(unsigned char), a*64, fp);
  fread(p8, sizeof(unsigned char), b*256, fp);

  cudaMemcpy(*patch4x4, p4, a*64*sizeof(unsigned char), cudaMemcpyHostToDevice);
  cudaMemcpy(*patch8x8, p8, b*256*sizeof(unsigned char), cudaMemcpyHostToDevice);



  free(p4);
  free(p8);

  fclose(fp);
}


void FreeAll(int *w_dev, int *h_dev, unsigned char *pyr_dev, unsigned char *s1_dev[4], unsigned char *c1_dev[4], unsigned char *patch4, unsigned char *patch8, unsigned char *s2_dev) {
  cudaFree(w_dev);
  cudaFree(h_dev);
  cudaFree(pyr_dev);
  cudaFree(s1_dev);

  cudaFree(patch4);
  cudaFree(patch8);
  cudaFree(s2_dev);

  for(int i=0; i<4; i++) {
    cudaFree(s1_dev[i]);
    cudaFree(c1_dev[i]);
  }

}






}



